﻿#region The MIT License
// The MIT License
// 
// Copyright (c) 2011 octalforty studios
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#endregion
using System.Diagnostics;
using System.Threading;

using NLog;

using octalforty.Staytuned.ComponentModel;
using octalforty.Staytuned.ObjectModel;
using octalforty.Staytuned.Resources;

namespace octalforty.Staytuned.Impl.ComponentModel
{
    /// <summary>
    /// Provides an implementation of <see cref="INotificationPublisher"/>.
    /// </summary>
    public class NotificationPublisher : INotificationPublisher
    {
        #region Private Members
        private readonly Logger log = LogManager.GetCurrentClassLogger();
        #endregion

        /// <summary>
        /// Gets or sets a reference to a <see cref="ITemplateProvider"/>, which is used to
        /// retrieve templates for notifications.
        /// </summary>
        public ITemplateProvider TemplateProvider { get; set; }

        /// <summary>
        /// Gets or sets a reference to a <see cref="IMessageFormatter"/>, which is used to
        /// transform <see cref="Notification"/> into <see cref="Message"/> objects.
        /// </summary>
        public IMessageFormatter MessageFormatter { get; set; }

        /// <summary>
        /// Gets or sets a reference to a <see cref="IMessagePublisher"/>, which is used to publish
        /// <see cref="Message"/> instances.
        /// </summary>
        public IMessagePublisher MessagePublisher { get; set; }

        public void PublishNotification(string eventDescriptor, Notification notification, params RecipientDescriptor[] recipients)
        {
            #region Tracing
            log.Info("publishing notification ('{0}') to {1} addresses", eventDescriptor, recipients.Length);
            var globalStopwatch = Stopwatch.StartNew();
            #endregion

            foreach(var recipient in recipients)
            {
                #region Tracing
                log.Debug("publishing notification ('{0}') to '{1}' (format: {2})", eventDescriptor, recipient.Address.EffectiveAddress, recipient.Format);
                var stopwatch = Stopwatch.StartNew();
                #endregion

                TryPublishNotificationInternal(eventDescriptor, recipient, notification);

                #region Tracing
                log.Debug("published notification ('{0}') to '{1}' (format: {2}) in {3}",
                    eventDescriptor, recipient.Address.EffectiveAddress, recipient.Format, stopwatch.Elapsed);
                #endregion
            }

            #region Tracing
            log.Info("published notification ('{0}') to {1} addresses in {2}", 
                eventDescriptor, recipients.Length, globalStopwatch.Elapsed);
            #endregion
        }

        private void TryPublishNotificationInternal(string eventDescriptor, RecipientDescriptor recipient, Notification notification)
        {
            try
            {
                var template = TemplateProvider.GetTemplate(eventDescriptor, recipient);
                if(template == null) 
                    throw new PublishNotificationException(
                        string.Format(Exceptions.CouldNotSelectTemplate, eventDescriptor, recipient.Address));

                var message = MessageFormatter.FormatMessage(template, notification, eventDescriptor, recipient);
                if(message == null)
                    throw new PublishNotificationException(
                        string.Format(Exceptions.CouldNotFormatMessage, eventDescriptor, recipient.Address));

                MessagePublisher.PublishMessage(message, recipient.Address);
            } // try
            catch(PublishMessageException e)
            {
                log.Error("could not publish notification ('{0}') to {1}: {2}", eventDescriptor, recipient, e, e);
            } // catch
        }

        public void PublishNotificationAsync(string eventDescriptor, Notification notification, 
            RecipientDescriptor[] addresses, PublishAsyncCallback publishAsyncCallback)
        {
            ThreadPool.QueueUserWorkItem(o => PublishNotification(eventDescriptor, notification, addresses));
        }

        /*public static INotificationPublisherService CreateInstance(string templatesRootPath, params INotificationSupportModule[] notificationSupportModules)
        {
            return CreateInstance(
                new FsTemplateSelectorService(templatesRootPath) {
                    FsTemplateReader = new FsXmlTemplateReader()
                }, 
                notificationSupportModules);
        }

        public static INotificationPublisherService CreateInstance(ITemplateSelectorService templateSelectorService, params INotificationSupportModule[] notificationSupportModules)
        {
            return new NotificationPublisherService {
                MessageFormatter = new MessageFormatterService(notificationSupportModules),
                MessagePublisher = new MessagePublisherService(notificationSupportModules),
                TemplateSelector = templateSelectorService
            };
        }*/
    }
}
